using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;

namespace M13.Phys
{
    public enum Interpolation
    {
        None,
        Interpolae
    }
    public class AABBCollider : MonoBehaviour
    {
        public AABB aabb;
        

        #region ---------配置信息---------

        public Vector3 center;
        public Vector3 size;
        public AABBType aabbType;
        public bool useGravity;
        public float gravityScale = 1f;
        public Interpolation interpolation = Interpolation.None;

        #endregion----------------------
        
        [Space(10)]
        [Header("调试参数")]
        
        #region --------调试参数--------
        
        [ReadOnly]
        public bool isGrounded;
        
        [ReadOnly]
        public Vector3 velocity;
        
        [ReadOnly]
        public float speed;
        #endregion----------------------
        
        private Vector3 _totalVector;
        private Vector3 _relativeOffsetOfOrigin;

        private const float Gravity = 9.8f;
        
        private List<Vector3> _vectorList = new List<Vector3>();


        private float _fixedRealTime;
        private float _lastFixedRealTime;
        
        private float _realTime;
        private float _lastRealTime;

        private Vector3 _fixedAABBPosition;
        private Vector3 _lastFixedAABBPosition;

        private float _lastSimulatedPhysicalFrameTime;
        /// <summary>
        /// 模拟的物理帧时刻，不是fixed，而是渲染帧跨过物理帧时，这个渲染帧就是模拟的物理帧时刻
        /// </summary>
        private float _simulatedPhysicalFrameTime;

        /// <summary>
        /// 模拟帧步长
        /// </summary>
        private float _simulatedPhysicalFrameDeltaTime = 0.02f;

        private float _lastSimulatedPhysicalFrameDeltaTime = 0.02f;
        private float _t;
        
        private Vector3 _renderingFramesMoveVector; 
        private Vector3 _lastRenderingFramesMoveVector;
        private Vector3 _lastTotalVector;
        private Vector3 _lastRenderingPos;

        
        private void Awake()
        {
            aabb = new AABB(center + transform.position, size, aabbType);
            DispersedBoundsManager.Register(aabb);
            
            
            _relativeOffsetOfOrigin = center;
        }

        private void Start()
        {
            transform.position = aabb.bound.center - _relativeOffsetOfOrigin;

            _fixedAABBPosition = _lastFixedAABBPosition = aabb.bound.center;
        }
        
        private void Update()
        {
            _lastRealTime = _realTime;
            _realTime = Time.realtimeSinceStartup;
            
            RenderInterpolation();
            

            velocity = (transform.position - _lastRenderingPos) / Time.deltaTime;
            speed = (transform.position - _lastRenderingPos).magnitude / Time.deltaTime;
        }

        private float _gravityTimer = 0f;
        private void FixedUpdate()
        {
            #region 必须在开始更新

            _lastFixedRealTime = _fixedRealTime;
            _fixedRealTime = Time.realtimeSinceStartup;
            
            _lastFixedAABBPosition = _fixedAABBPosition;
            _fixedAABBPosition = aabb.bound.center;

            #endregion

            
            HandleGravity();
            
            

            #region 必须在结尾更新
            _lastTotalVector = _totalVector;
            Vector3 v = Vector3.zero;
            for(int i = 0; i < _vectorList.Count; i++)
                v += _vectorList[i];
            _totalVector = v;
            aabb.Move(ref _totalVector);
            _vectorList.Clear();
            
            #endregion
            
        }

        private void RenderInterpolation()
        {
            if (interpolation == Interpolation.None)
            {
                transform.position = aabb.bound.center - _relativeOffsetOfOrigin;
            }
            else if (interpolation == Interpolation.Interpolae)
            {
                //在物理帧更新时的第一渲染帧计算模拟物理帧
                if (_lastRealTime < _fixedRealTime && _realTime > _fixedRealTime)
                {
                    _lastSimulatedPhysicalFrameTime = _simulatedPhysicalFrameTime;
                    _simulatedPhysicalFrameTime = _realTime;
                    _lastSimulatedPhysicalFrameDeltaTime = _simulatedPhysicalFrameDeltaTime;
                    _simulatedPhysicalFrameDeltaTime = _simulatedPhysicalFrameTime - _lastSimulatedPhysicalFrameTime;
                    
                    _lastRenderingFramesMoveVector = _renderingFramesMoveVector;
                    _renderingFramesMoveVector = Vector3.Lerp(Vector3.zero, _lastTotalVector, 1f);
                }
                else
                {
                    _t = (_realTime - _simulatedPhysicalFrameTime) / _simulatedPhysicalFrameDeltaTime;
                    _lastRenderingFramesMoveVector = _renderingFramesMoveVector;
                    _renderingFramesMoveVector = Vector3.Lerp(Vector3.zero, _totalVector, _t);
                }
                _lastRenderingPos = transform.position;
                transform.position = _lastFixedAABBPosition - _relativeOffsetOfOrigin + _renderingFramesMoveVector;
                if (_lastRealTime < _fixedRealTime && _realTime > _fixedRealTime)
                    _renderingFramesMoveVector = Vector3.zero;
            }
        }
        
        private void HandleGravity()
        {
            isGrounded = aabb.BottomContact();
            
            if(useGravity)
            {
                Vector3 wantMove;
                if (isGrounded)
                {
                    _gravityTimer = 0f;
                    wantMove = Vector3.zero;
                }
                else
                {
                    wantMove = new Vector3(0, -Gravity * gravityScale * Time.fixedDeltaTime * (2 * _gravityTimer - Time.fixedDeltaTime), 0);
                    _gravityTimer += Time.fixedDeltaTime;
                    _vectorList.Add(wantMove);
                }
            }
        }
        
        /// <summary>
        /// 必须在FixedUpdate中引用
        /// </summary>
        /// <param name="move">移动的向量，单位是秒</param>
        public void SimpleMove(Vector3 move)
        {
            _vectorList.Add(move * Time.fixedDeltaTime);
        }
        
        

        private void OnDrawGizmos()
        {

            if (!Application.isPlaying)
            {
                Gizmos.color = Color.white;
                Gizmos.DrawWireCube(center + transform.position, size);
                return;
            }
            
            Gizmos.color = Color.white;
            Gizmos.DrawWireCube(aabb.GetCenter(), aabb.GetSize());
        }
    }
}